home *** CD-ROM | disk | FTP | other *** search
- #import "Alexandra.h"
- #import "ArticleViewControl.h"
- #import <misckit/MiscAppDefaults.h>
- #import "response_codes.h"
- #import "FaceView.h"
- #import <misckit/MiscClockView.h>
- #import "parse-header.h"
- #import "rfc822realname.h"
- #include <ctype.h>
-
- #define QUOTESTRING ">"
- #define ISEMPTY(string) ((string==NULL)||(*(string)=='\0'))
- #define FORCEBREAK "\n\t -•1234567890>#}:"
-
- @implementation ArticleViewControl
-
- - init
- {
- [super init];
- noArticle=TRUE;
- rot13=FALSE;
- [ERROR_MANAGER addObserver:self
- selector:@selector(updateText)
- forError:ENOTEPrefsChanged];
- [self updateText];
-
- return self;
- }
-
- - awakeFromNib
- {
- [theText setFontPanelEnabled:FALSE];
- [clockView setMilitaryTime:[NXApp defaultBoolValue:"24HourClock"]];
- [clockView setHide:YES];
-
- return self;
- }
-
- - free
- {
- if(articleHeader)
- free(articleHeader);
- if(articleBody)
- free(articleBody);
- [ERROR_MANAGER removeObserver:self forError:ENOTEPrefsChanged];
-
- return [super free];
- }
-
- - updateText
- {
- [self displayArticleScrollUp:NO];
-
- return self;
- }
-
- - (int)loadArticle:(Article *)theArticle fromGroup:(const char *)theGroup
- {
- int statusCode;
- time_t t;
-
- rot13=FALSE;
- // get article header + body
- if(articleHeader)
- free(articleHeader);
- statusCode=[nntpServer loadArticleHeader:theArticle toString:&articleHeader];
- if(statusCode!=OK_HEAD){
- return statusCode;
- }
-
- subject=[theArticle header]->fieldBody[SUBJECT];
- from=[theArticle header]->fieldBody[FROM];
- organization=[theArticle header]->fieldBody[ORGANIZATION];
-
-
- if(articleBody)
- free(articleBody);
- statusCode=[nntpServer loadArticleBody:theArticle toString:&articleBody];
- if(statusCode!=OK_BODY){
- return statusCode;
- }
-
- noArticle=FALSE;
- [self displayArticleScrollUp:YES];
-
- t=[theArticle time];
- if(![imageView showFaceForName:[theArticle header]->fieldBody[FROM]]){
- const char *r=[theArticle header]->fieldBody[REPLY_TO];
- if(r && *r)
- [imageView showFaceForName:r];
- }
- [[[clockView setHide:FALSE] setTime:localtime(&t)] display];
-
- return OK_BODY;
- }
-
-
- - displayArticleScrollUp:(BOOL)scroll
- {
- Font *textF,*sigF;
- const char *signature,*eob,*p,*thisLine;
- int len,rows,alnum,others;
- NXSize visibleSize;
- NXRect theUpperRect;
- char *tmpArticleBody=articleBody;
-
- signatureDetection=[NXApp defaultBoolValue:DEFAULT_SIG_DETECTION];
- rewrapping=[NXApp defaultBoolValue:DEFAULT_REWRAP_ARTICLE_TEXT];
- headerMode=[NXApp defaultIntValue:DEFAULT_HEADER_MODE];
-
- if(noArticle)
- return self;
-
- // rot13 article body?
- if(rot13){
- int i,j;
- j=strlen(articleBody);
- tmpArticleBody=malloc((j+2)*sizeof(char));
- for(i=0;i<j;i++) {
- if(islower(articleBody[i]))
- tmpArticleBody[i] = (((articleBody[i]-'a')+13)%26)+'a';
- else if(isupper(articleBody[i]))
- tmpArticleBody[i] = (((articleBody[i]-'A')+13)%26)+'A';
- else
- tmpArticleBody[i]=articleBody[i];
- }
- }
-
- [[theText window] disableDisplay];
- [theText setAutodisplay:NO];
- [theText setText:""];
-
- textF=[NXApp defaultFont:DEFAULT_ARTICLE_FONT];
-
- sigF=[Font userFixedPitchFontOfSize:0 matrix:NX_FLIPPEDMATRIX];
- [theText setFont:textF];
-
- //load text in text instance
- if(headerMode==NEWSPAPER_HEADER){
- [self writeNewspaperHeader];
- }
- else if(headerMode==FULL_HEADER){
- [theText setText:articleHeader];
- }
- else if(headerMode==SMALL_HEADER){
- [self writeSelectedHeader];
- }
- len=[theText textLength];
- [theText setSel:len :len];
- [theText replaceSel:"\n"];
-
- signature=NULL;
- len=strlen(tmpArticleBody);
- if(len>4){
- eob=tmpArticleBody+len-1;
- signature=NULL;
- //find signature
- if(signatureDetection){
- rows=0;
- for(p=tmpArticleBody+len-4;p>tmpArticleBody && rows<16;p--)
- if(p[0]=='\n'){
- if(p[1]=='-' && p[2]=='-' && p[strspn(p+1,"\t -")+1]=='\n'){
- eob=p;
- signature=p+3;
- if(*signature=='-')
- signature++;
- break;
- }
- else
- rows++;
- }
- }
-
- thisLine=tmpArticleBody;
- do{
- alnum=others=1;
- for(p=thisLine;p<eob && p[0]!='\n';p++)
- if(NXIsAlNum(p[0]) || p[0]==' ')
- alnum++;
- else
- others++;
-
- if(rewrapping && p!=eob && alnum>40 && alnum/others>3 && !strchr(FORCEBREAK,p[1]))
- {
- [theText appendString:thisLine length:p-thisLine withFont:textF];
- if(p[-1]!=' ')
- [theText appendString:" " withFont:textF];
- }
- else
- [theText appendString:thisLine length:p-thisLine+1 withFont:textF];
-
- thisLine=p+1;
- }
- while(p<eob);
-
- if(signatureDetection && signature)
- {
- [theText appendString:"\n" withFont:sigF];
- [theText appendString:signature withFont:sigF];
- }
- }
- else
- [theText appendString:tmpArticleBody length:len withFont:textF];
-
- if(scroll){
- [[[theText superview] superview] getContentSize:&visibleSize];
- NXSetRect(&theUpperRect,0.0,0.0,visibleSize.width,visibleSize.height);
- [theText scrollRectToVisible:&theUpperRect];
- }
-
- [theText setAutodisplay:YES];
- [[theText window] reenableDisplay];
- [[theText window] display];
-
- if(rot13)
- free(tmpArticleBody);
-
- return self;
- }
-
- - clear
- {
- [theText setAutodisplay:NO];
- [theText setText:""];
- [[theText setAutodisplay:YES] display];
-
- noArticle=TRUE;
- [imageView showFaceForName:NULL];
- [[clockView setHide:TRUE] display];
- if(articleBody)
- free(articleBody);
- if(articleHeader)
- free(articleHeader);
- articleBody=NULL;
- articleHeader=NULL;
-
- return self;
- }
-
- - saveAs:sender
- {
- id panel;
- int fd;
- NXStream *theStream;
- static char *dir=NULL;
-
- if(dir==NULL)
- dir=NXCopyStringBuffer([NXApp defaultValue:DEFAULT_SAVE_PATH]);
- if([theText textLength]==0 || noArticle)
- return self;
- panel=[SavePanel new];
- if([panel runModalForDirectory:dir file:""]!=NX_CANCELTAG){
- free(dir);
- dir=NXCopyStringBuffer([panel filename]);
- }
- else
- return nil;
-
- //Save
- fd=open([panel filename],O_WRONLY|O_CREAT|O_TRUNC,0666);
- if(fd<0){
- NXRunAlertPanel("ALEXANDRA","Can't save file: %s",NULL,NULL,NULL,strerror(errno));
- return self;
- }
- theStream=NXOpenFile(fd,NX_WRITEONLY);
- NXWrite(theStream,articleHeader,strlen(articleHeader));
- NXWrite(theStream,"\n",1);
- NXWrite(theStream,articleBody,strlen(articleBody));
- NXClose(theStream);
- close(fd);
- return self;
- }
-
- - printText:sender
- {
- if([theText textLength]!=0){
- [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
- [theText printPSCode:self];
- }
- return self;
- }
-
- - (const char *)articleBody
- {
- if(noArticle)
- return NULL;
- return articleBody;
- }
-
- - (const char *)articleHeader
- {
- if(noArticle)
- return NULL;
- return articleHeader;
- }
-
- - writeBody:(NXStream *)aStream
- {
- if(articleBody)
- NXWrite(aStream,articleBody,strlen(articleBody));
-
- return self;
- }
-
- - writeQuotedText:(NXStream *)aStream
- {
- int i;
- int bodyLen;
- BOOL wasNewline;
- const char *quotestring;
-
- if(!articleBody)
- return self;
-
- quotestring=[NXApp defaultValue:DEFAULT_QUOTING_PREFIX];
- if(!quotestring){
- [NXApp setDefault:DEFAULT_QUOTING_PREFIX to:QUOTESTRING];
- quotestring=[NXApp defaultValue:DEFAULT_QUOTING_PREFIX];
- }
-
- bodyLen=strlen(articleBody);
- wasNewline=TRUE;
- for(i=0;i<bodyLen;i++){
- if(wasNewline)
- NXPrintf(aStream,"%s",quotestring);
- NXPutc(aStream,articleBody[i]);
- if(articleBody[i]=='\n')
- wasNewline=TRUE;
- else
- wasNewline=FALSE;
- }
-
- return self;
- }
-
- - writeSelectedHeader
- {
- char **selectedHeaders;
- char *field_names,*f_buffer,**pattern;
- const char *default_headers;
- int count=0;
- int num_h=0;
- int i;
- BOOL start=TRUE;
- NXStream *aStream=NXOpenMemory(NULL,0,NX_READWRITE);
-
- default_headers=[NXApp defaultValue:DEFAULT_HEADER_FILTER];
- if(*default_headers=='\0'){
- return self;
- }
-
- field_names=NXCopyStringBuffer(default_headers);
- for(f_buffer=field_names;*f_buffer!='\0';f_buffer++)
- if(*f_buffer==':')
- num_h++;
- selectedHeaders=calloc(num_h,sizeof(char**));
- pattern=calloc(num_h,sizeof(char**));
-
- f_buffer=field_names; i=0;
- for(;*f_buffer!='\0';f_buffer++){
- if(start){
- pattern[i]=f_buffer;
- start=FALSE;
- i++;
- }
- else if(*f_buffer==':'){
- start=TRUE;
- *f_buffer='\0';
- }
- }
-
- parse_header(articleHeader,strlen(articleHeader),selectedHeaders,pattern,num_h,NO);
-
- i=0;
- for(count=0;count<num_h;count++)
- if(selectedHeaders[count]!=NULL){
- i++;
- NXPrintf(aStream,"%s: %s\n",pattern[count],selectedHeaders[count]);
- free(selectedHeaders[count]);
- }
- NXPrintf(aStream,"\n");
-
- free(field_names);
- free(selectedHeaders);
- free(pattern);
- NXSeek(aStream,0,NX_FROMSTART);
- [theText readText:aStream];
- NXCloseMemory(aStream,NX_FREEBUFFER);
-
- return self;
- }
-
- - writeNewspaperHeader
- {
- Font *titleF,*authorF;
-
- NXRect visibleRect;
- int titleFsize=24;
-
- [theText getVisibleRect:&visibleRect];
-
- authorF=[Font boldSystemFontOfSize:14 matrix:NX_FLIPPEDMATRIX];
-
- if(!ISEMPTY(subject)){
- while(([titleF=[Font boldSystemFontOfSize:titleFsize matrix:NX_FLIPPEDMATRIX]
- getWidthOf:subject]>NX_WIDTH(&visibleRect)-10) && titleFsize>12)
- titleFsize-=2;
-
- [theText appendString:subject withFont:titleF];
- [theText appendString:"\n" withFont:titleF];
- }
- if(!ISEMPTY(from)){
- int nameFrom,to;
-
- rfc822_realname(from,&nameFrom,&to);
- [theText appendString:"by " withFont:authorF];
- [theText appendString:from+nameFrom length:to-nameFrom+1 withFont:authorF];
- if(!ISEMPTY(organization)){
- [theText appendString:", " withFont:authorF];
- [theText appendString:organization withFont:authorF];
- }
- [theText appendString:"\n" withFont:authorF];
- }
-
- return self;
- }
-
- -(id)theText
- {
- return theText;
- }
-
- - setModeTo:(int)mode
- {
- [NXApp setDefault:DEFAULT_HEADER_MODE toInt:mode];
- if(!noArticle)
- [self displayArticleScrollUp:NO];
-
- return self;
- }
-
- - showHeader:sender
- {
- [self setModeTo:FULL_HEADER];
- return self;
- }
-
- - hideHeader:sender
- {
- [self setModeTo:NO_HEADER];
- return self;
- }
-
- - smallHeader:sender
- {
- [self setModeTo:SMALL_HEADER];
- return self;
- }
-
- - newspaperHeader:sender
- {
- [self setModeTo:NEWSPAPER_HEADER];
- return self;
- }
-
- - (BOOL)headermodeCellEnabled:menuCell
- {
- if((headerMode!=FULL_HEADER)&&([menuCell action]==@selector(showHeader:)))
- return TRUE;
- else if((headerMode!=NO_HEADER)&&([menuCell action]==@selector(hideHeader:)))
- return TRUE;
- else if((headerMode!=SMALL_HEADER)&&([menuCell action]==@selector(smallHeader:)))
- return TRUE;
- else if((headerMode!=NEWSPAPER_HEADER)&&([menuCell action]==@selector(newspaperHeader:)))
- return TRUE;
- return FALSE;
- }
-
- - rot13:sender
- {
- rot13=!rot13;
-
- [self displayArticleScrollUp:NO];
-
- return self;
- }
-
- @end
-